package com.frontalcode 
{
	import flash.events.*;
	import flash.xml.*;
	
	/**                  
     * The Util class holds miscellaneous utility functions.
     */
    public class Util
    {
		/**            
		 * @private
		 */
		static public var isEmpty					: RegExp = /^\s*$/;

		/**            
		 * @private
		 */
		static public var integer 					: RegExp = /^-?\d+$/;

		/**            
		 * @private
		 */
		static public var posInteger 				: RegExp = /^\d+$/;

		/**            
		 * @private
		 */
		static public var decimal 					: RegExp = /^-?(\d+(\.\d+)?|\.\d+)$/;

		/**            
		 * @private
		 */
		static public var decimalPx					: RegExp = /^-?(\d+(\.\d+)?|\.\d+)(px)?$/;

		/**            
		 * @private
		 */
		static public var decimalPxOrPercent		: RegExp = /^-?(\d+(\.\d+)?|\.\d+)(px|%)?$/;

		/**            
		 * @private
		 */
		static public var lengthSpec				: RegExp = /^((-?\d+(\.\d+)?|\.\d+)(px|%)?|auto)(\s+leftover)?$/;

		/**            
		 * @private
		 */
		static public var upToFourLengthSpec 		: RegExp = /^(((-?\d+(\.\d+)?|\.\d+)(px|%)?|auto)(\s+leftover)?\s+){0,3}((-?\d+(\.\d+)?|\.\d+)(px|%)?|auto)(\s+leftover)?$/;

		/**            
		 * @private
		 */
		static public var booleanVal				: RegExp = /^yes|no|true|false$/;

		/**            
		 * @private
		 */
		static public var colorVal					: RegExp = /^#[0-9a-f]{6}|[a-zA-Z]+$/i;

		/**            
		 * @private
		 */
		static public var isTrue					: RegExp = /^\s*(yes|true)\s*$/;

		/**            
		 * @private
		 */
		static public var isFalse					: RegExp = /^\s*(no|false)\s*$/;

		/**            
		 * @private
		 */
		static public var isAbsoluteURL				: RegExp = /^https?:\/\//;
		
		static public function trim ( value : String ) : String 
		{
			// XXX Problems with this RegExp when the value is anything but
			// numbers and letters so do it the hard way.
			//
			// var result : String = value.replace ( /^\s*(.*)\b\s*$/, "$1" );
			var char : String;
			var firstChar : int = 0;
			while ( ( char = value.charAt ( firstChar ) ) == " " || char =="\t" || char =="\r" || char =="\n" ) firstChar++;
			var lastSpace : int = value.length;
			while ( ( char = value.charAt ( lastSpace - 1 ) ) == " " || char =="\t" || char =="\r" || char =="\n" ) lastSpace--;
			return value.substring ( firstChar, lastSpace );
		}
		
		/*
			Function: isNumber
			
				Return true if the value is a number otherwise false. This is
				very similar to ! isNaN ( ) but does not consider true and false
				to be convertible.
				
			Parameters:
			
				value	- The value to test.
				
			Returns:
			
				true or false.
		*/
		static public function isNumber ( value : * ) : Boolean
		{
			return value === null || value === true || value === false ? false : ! isNaN ( value );
		}
	
		/*
			Function: splitWithGroupings
			
				String.split puts the grouping parentheses values of a RegExp 
				into the result so we want to combine them with their 
				predecessors or ancestors.
				
				For example, in Transitioner, we split the value of the
				transitions attribute with this RegExp: \s*([^\\]?),\s*.
				Normally a split would then add the parenthetical match into the
				resulting array but we want it grouped with the preceding split
				result so we use this function like so:
				
				var specs : Array = Util.splitWithGroupings ( getAttribute ( "transitions" ), /\s*([^\\]?),\s* /, [ "pre" ] );
				
				(Note in the example we added a space before the RegExp's 
				closing "/" so that it isn't a close comment symbol.)
				
			Parameters:
			
				value			- the string to split
				splitter		- a regular expression with grouping parentheses
				groupings		- an array of entries (the same number as there
								  are grouping parentheses) with values of 
								  "pre", "succ" or null to add the result to the
								  predecessor, the successor or to discard it.
								  
			Returns:
			
				The combined array.
		*/
		static public function splitWithGroupings ( value : String, splitter : RegExp, groupings : Array ) : Array
		{
			var tmp : Array = value.split ( splitter );
			
			if ( tmp.length <= 1 || groupings == null || groupings.length == 0 ) return tmp;
			
			var result : Array = new Array ( 1 + ( tmp.length - 1 ) / ( groupings.length + 1 ) );
			for ( var i : uint = 0; i < result.length; i++ ) result [ i ] = "";
			var index : int = -1;
			
			for ( i = 0; i < tmp.length; i++ )
			{
				if ( i % ( groupings.length + 1 ) == 0 ) 
				{
					index++;
					result [ index ] += tmp [ i ];
				}
				else
				{
					switch ( groupings [ ( i % ( groupings.length + 1 ) ) - 1 ] )
					{
						case "pre":
							result [ index ] += tmp [ i ];
							break;
						case "succ":
							result [ index + 1 ] += tmp [ i ];
							break;
					}
				}
			}
			
			return result;
		}
		
		static public function capitalize ( value : String ) : String
		{
			return value.replace ( /\b(\w)/g, function ( ) : String { return arguments [ 1 ].toUpperCase ( ); } );
		}
		
		/**            
		 * @private
		 */
		static internal function eventBlockerHandler ( e : Event ) : void
		{
			e.preventDefault ( );
			e.stopImmediatePropagation ( );
		}
		
		static public function htmlEscape ( msg : String ) : String
		{
			var xml : XML = <tmp />;
			xml.@tmp = msg;
			return xml.@tmp.toXMLString ( );
		}
		
		/*
			Function: filterNodes
			
				This function may be used to perform complex queries on an XML
				node or XMLList object. The function is placed into the 
				following E4X construct to generate the results:
				
					xml.( filter ( valueOf ( ) ) )
					
				The filter function has the following signature:
				
					internal function ( xmlNode : Node ) : Boolean
					
				It should return true if xml matches the filter otherwise false.
				
				So for example to get the descendants of a node that have the
				class attribute set to "test":
				
					var result : XMLList = filterNodes ( node.descendants ( ), function ( xmlNode )
						{
							return xmlNode.attribute ( "class" ) == "test";
						} );
				
			Parameters:
			
				xml			- the XML node or XMLList object to filter
				filter		- the funciton performing the filter
								  
			Returns:
			
				An XMLList of the filtered nodes.
		*/
		static public function filterNodes ( xml : *, filter : Function ) : XMLList
		{
			return xml.( filter ( valueOf ( ) ) );
		}
    }
}

